<?php


namespace EchoPHP\Cache;


class MemcacheStore implements CacheContract {

    /**
     * memcached实例
     * @var \Memcached
     */
    protected $memcached;

    /**
     * 键名前缀
     * @var
     */
    protected $prefix;

    /**
     * memcache键名命名空间
     * @var
     */
    protected $namespace;

    public function __construct($memcached, $namespace, $prefix = '') {
        $this->setPrefix($prefix);
        $this->setNamespace($namespace);

        $this->memcached = $memcached;
    }

    /**
     * @inheritDoc
     */
    public function has($key) {
        return !is_null($this->get($key));
    }

    /**
     * @inheritDoc
     */
    public function get($key, $default = null) {
        $value = $this->memcached->get($this->getRealKey($key));
        if ($value === false) {
            return $default;
        }
        return $value;
    }

    /**
     * @inheritDoc
     */
    public function pull($key, $default = null) {
        $value = $this->get($key, $default);
        if (!is_null($value)) {
            $this->delete($key);
        }
        return $value;
    }

    /**
     * @inheritDoc
     */
    public function set($key, $value, $seconds = null) {
        return $this->memcached->set($this->getRealKey($key), $value, $this->expirationToTimestamp($seconds));
    }

    /**
     * @inheritDoc
     */
    public function add($key, $value, $seconds = null) {
        return $this->memcached->add($this->getRealKey($key), $value, $this->expirationToTimestamp($seconds));
    }

    /**
     * @inheritDoc
     */
    public function increment($key, $step = 1) {
        if (!$this->has($key)) {
            $this->set($key, 0);
        }
        return $this->memcached->increment($this->getRealKey($key), $step);
    }

    /**
     * @inheritDoc
     */
    public function decrement($key, $step = 1) {
        if (!$this->has($key)) {
            $this->set($key, 0);
        }
        return $this->memcached->decrement($this->getRealKey($key), $step);
    }

    /**
     * @inheritDoc
     */
    public function delete($key) {
        $key = $this->getRealKey($key);
        return $this->memcached->delete($key);
    }

    /**
     * @inheritDoc
     */
    public function flush() {
        return $this->memcached->set($this->namespace, time());
    }

    /**
     * 获取完整的缓存键名
     * 此处使用模拟命名空间，即键名由 前缀 + 命名空间 + 自定义键名 组成，如果想要指定的统一前缀memcache失效，由于memcache本身不支持也不建议直接失效数据，所以引入模拟命名空间概念
     * 即： 键名为 prefix:1:*，由于很多数据在一台memcache上，单个失效比较麻烦，一次性失效又会影响其他数据，此时修改命名空间 prefix:2:* 虽然键名还在，但不会被取到也就失效，基于memcache的失效机制，这部分数据最终会被删除，从而达到批量删除的目的
     * @param $key
     * @return string
     */
    protected function getRealKey($key) {
        $nameSpaceKey = $this->memcached->get($this->namespace);
        if (!$nameSpaceKey) {
            $nameSpaceKey = time();
            $this->memcached->set($this->namespace, $nameSpaceKey);
        }
        return $this->prefix . $nameSpaceKey . ':' . $key;
    }

    /**
     * 获取有效期对应时间戳，memcache有效期定义 0 永久有效  小于 60 * 60 * 24 * 30 就是剩余有效秒  大于的认为是过期的时间戳
     * @param int $seconds
     * @return int
     */
    protected function expirationToTimestamp($seconds) {
        $seconds = intval($seconds);

        return $seconds > 0 ? $seconds : 0;
    }

    /**
     * 获取前缀
     * @return mixed
     */
    public function getPrefix() {
        return $this->prefix;
    }

    /**
     * 获取命名空间名称
     * @return mixed
     */
    public function getNamespace() {
        return $this->namespace;
    }

    /**
     * 设置memcache缓存的命名空间
     * @param $namespace
     * @throws \Exception
     */
    public function setNamespace($namespace) {
        $this->namespace = $this->prefix . ($namespace ?: config('app_name', 'EchoPHP'));
    }

    /**
     * 设置前缀
     * @param $prefix
     */
    protected function setPrefix($prefix) {
        $this->prefix = empty($prefix) ? '' : ($prefix . ':');
    }
}